package com.cincc.cinccimdemo.common.record;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.util.Log;

import androidx.core.app.ActivityCompat;

import com.cincc.im.util.ImManager;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class RecorderManager {
    private static String TAG = "OnlyAudioRecorder";
    private int AudioSource = MediaRecorder.AudioSource.MIC;
    private int SampleRate = 16000;
    private int Channel = AudioFormat.CHANNEL_IN_MONO;
    private int EncodingType = AudioFormat.ENCODING_PCM_16BIT;

    private String pcmPath;
    private String wavPath;

    private int bufferSizeInByte = 0;
    private AudioRecord audioRecorder = null;
    private Boolean isRecord = false;

    OnAudioRecordListener onAudioRecordListener;

    private static RecorderManager manager;

    public static RecorderManager getInstance() {
        if (null == manager) {
            synchronized (RecorderManager.class) {
                if (null == manager) {
                    manager = new RecorderManager();
                }
            }
        }
        return manager;
    }

    private boolean initRecorder(Context context) {
        long timeSpans = System.currentTimeMillis();
        pcmPath = context.getExternalFilesDir(null).toString() + "/record/" + timeSpans + ".pcm";
        wavPath = context.getExternalFilesDir(null).toString() + "/record/" + timeSpans + ".wav";
        Log.e("wavPath===>", wavPath);

        bufferSizeInByte = AudioRecord.getMinBufferSize(SampleRate, Channel, EncodingType);
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
        audioRecorder = new AudioRecord(
                AudioSource, SampleRate, Channel,
                EncodingType, bufferSizeInByte
        );
        return true;
    }

    public void startRecord(Context context, OnAudioRecordListener onAudioRecordListener) {
        this.onAudioRecordListener = onAudioRecordListener;
        if (!isRecord) {
            boolean init = initRecorder(context);
            if (init) {
                audioRecorder.startRecording();
                isRecord = true;
                ImManager.getInstance().getWorkHandler().post(() -> {
                    try {
                        writeDateToFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

    private void writeDateToFile() throws IOException {
        byte[] audioData = new byte[bufferSizeInByte];
        File file = new File(pcmPath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (file.exists()) {
            file.delete();
        }
        file.createNewFile();
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        int length = 0;
        while (isRecord && audioRecorder != null) {
            length = audioRecorder.read(audioData, 0, bufferSizeInByte);//获取音频数据
            if (AudioRecord.ERROR_INVALID_OPERATION != length) {
                out.write(audioData, 0, length);
                double value = getDecibelForPcm(audioData, length);
                Log.d(TAG, "writeDateToFile: 分贝值是 " + value);
                if (length != 0)
                    onAudioRecordListener.dbResult(value);
                out.flush();
            }
        }
        out.close();
    }

    private double getDecibelForPcm(byte[] data, int dataLength) {
        long sum = 0;
        long temp = 0;
        for (int i = 0; i < data.length; i += 2) {
            temp = (data[i + 1] * 128 + data[i]); //累加求和
            temp *= temp;
            sum += temp;
        }

        //平方和除以数据长度，得到音量大小
        double square = sum / (double) dataLength; //音量大小
        Log.d(TAG, "writeDateToFile1: square = " + square + "  sum =" + sum + "  dataLength = " + dataLength);
        double result = 10 * Math.log10(square * 2); //分贝值
        return result;
    }

    private double calculateVolume(byte[] buffer) {
        double sumVolume = 0;
        double avgVolume = 0;
        double volume = 0;
        for (int i = 0; i < buffer.length; i += 2) {
            int v1 = buffer[i] & 0xFF;
            int v2 = buffer[i + 1] & 0xFF;
            int temp = v1 + (v2 << 8);// 小端
            if (temp >= 0x8000) {
                temp = 0xffff - temp;
            }
            sumVolume += Math.abs(temp);
        }
        avgVolume = sumVolume / buffer.length / 2;
        volume = Math.log10(1 + avgVolume) * 10;
        return volume;
    }


    public VoiceInfo stopRecord(boolean isDelete) {
        copyWaveFile(pcmPath, wavPath);
        audioRecorder.stop();
        audioRecorder.release();
        isRecord = false;
        audioRecorder = null;
        if (isDelete) {
            new File(pcmPath).delete();
            new File(wavPath).delete();
        }
        int length = getAudioTime(wavPath);
        return new VoiceInfo(wavPath, length / 1000);
    }

    //将pcm格式的文件转换为WAV格式的
    private void copyWaveFile(String pcmPath, String wavPath) {
        try {
            FileInputStream fileIn = new FileInputStream(pcmPath);
            FileOutputStream fileOut = new FileOutputStream(wavPath);
            byte[] data = new byte[bufferSizeInByte];
            long totalAudioLen = fileIn.getChannel().size();
            long totalDataLen = totalAudioLen + 36;
            writeWaveFileHeader(fileOut, totalAudioLen, totalDataLen);
            int count = fileIn.read(data, 0, bufferSizeInByte);
            while (count != -1) {
                fileOut.write(data, 0, count);
                fileOut.flush();
                count = fileIn.read(data, 0, bufferSizeInByte);
            }
            fileIn.close();
            fileOut.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //添加WAV格式的文件头
    private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen) {
        int channels = 1;
        int byteRate = 16 * SampleRate * channels / 8;
        byte[] header = new byte[44];
        header[0] = 'R';
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) (totalDataLen >> 8 & 0xff);
        header[6] = (byte) (totalDataLen >> 16 & 0xff);
        header[7] = (byte) (totalDataLen >> 24 & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f';
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16;
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1;
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (SampleRate & 0xff);
        header[25] = (byte) (SampleRate >> 8 & 0xff);
        header[26] = (byte) (SampleRate >> 16 & 0xff);
        header[27] = (byte) (SampleRate >> 24 & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) (byteRate >> 8 & 0xff);
        header[30] = (byte) (byteRate >> 16 & 0xff);
        header[31] = (byte) (byteRate >> 24 & 0xff);
        header[32] = (2 * 16 / 8);
        header[33] = 0;
        header[34] = 16;
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) (totalAudioLen >> 8 & 0xff);
        header[42] = (byte) (totalAudioLen >> 16 & 0xff);
        header[43] = (byte) (totalAudioLen >> 24 & 0xff);
        try {
            out.write(header, 0, 44);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private int getAudioTime(String filePath) {
        int length;
        MediaPlayer tmp = new MediaPlayer();
        try {
            tmp.setDataSource(filePath);
            tmp.prepare();
            length = tmp.getDuration();
        } catch (IOException e) {
            e.printStackTrace();
            tmp.release();
            return 0;
        }
        tmp.release();
        return length;
    }

    //分贝值监听
    public interface OnAudioRecordListener {
        void dbResult(double db);
    }
}


